package progress.exa38.nio;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.Channel;
import java.nio.channels.FileChannel;

/**
 * 非阻塞式IO
 */
public class NioStudy {

    /**
     * Nio机制实例学习
     * @param src
     * @param dst
     * @throws IOException
     */
    public static void copyFileUseNIO(String src,String dst) throws IOException {
        //声明源文件和目标文件
        FileInputStream fi = new FileInputStream(src);
        FileOutputStream fo = new FileOutputStream(dst);
        //获得传输通道channel
        FileChannel inChannel = fi.getChannel();
        FileChannel outChannel = fo.getChannel();
        //获得容器buffer
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        // 非阻塞式,可以实现边读边写
        while(true) {
            // 这里非阻塞返回
            int eof = inChannel.read(buffer);
            //判断是否读完文件
            if(eof == -1){
                break;
            }
            //重设一下buffer的position=0，limit=position
            buffer.flip();
            //开始写
            outChannel.write(buffer);
            //写完要重置buffer，重设position=0,limit=capacity
            buffer.clear();
        }
        inChannel.close();
        outChannel.close();
        fi.close();
        fo.close();
    }

    public static void main(String[] args) throws IOException {
        copyFileUseNIO("error.txt","output.txt");
    }



}

interface ChannelHandler {
    void channelReadable(Channel channel);
    void channelWritable(Channel channel);
}

/**
 * NIO的读写函数可以立刻返回，这就给了我们不开线程利用CPU的最好机会：
 * 如果一个连接不能读写（socket.read()返回0或者socket.write()返回0），我们可以把这件事记下来，记录的方式通常是在Selector上注册标记位，然后切换到其它就绪的连接（channel）继续进行读写。
 * Java的Selector对于Linux系统来说，有一个致命限制：同一个channel的select不能被并发的调用。因此，如果有多个I/O线程，必须保证：一个socket只能属于一个IoThread，而一个IoThread可以管理多个socket。
 */
//IO线程主循环:
//class IoThread extends Thread{
//    Selector selector = null;
//    public void run(){
//        Channel channel;
//        while(channel = selector.select()){//选择就绪的事件和对应的连接
//            if(channel.event == accept){
//                registerNewChannelHandler(channel);//如果是新连接，则注册一个新的读写处理器
//            }
//            if(channel.event==write){
//                getChannelHandler(channel).channelWritable(channel);//如果可以写，则执行写事件
//            }
//            if(channel.event==read){
//                getChannelHandler(channel).channelReadable(channel);//如果可以读，则执行读事件
//            }
//        }
//    }
//    Map<Channel,ChannelHandler> handlerMap;//所有channel的对应事件处理器
//}